package ca.venasse.pinventory.client.zk.converter;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.metainfo.Annotation;
import org.zkoss.zk.ui.sys.ComponentCtrl;
import org.zkoss.zkplus.databind.TypeConverter;

/**
 * <p>
 * Convert a value object which takes a {@link java.lang.String} in a 'create'
 * static method. Converts the value object to a {@link java.lang.String} using
 * the 'asString' method.
 * </p>
 * <p>
 * Must specify the value object to instantiate through the 'create' annotation.
 * </p>
 * 
 * <pre>
 * &lt;textbox value="@{name, converter='ca.venasse.pinventory.client.zk.converter.StringValueObjectTypeConverter'}" self="@{create(ca.venasse.pinventory.common.Name)}"/&gt;
 * </pre>
 * 
 * @author Seamus Venasse
 */
public class StringValueObjectTypeConverter implements TypeConverter {

	@Override
	public Object coerceToBean(Object val, Component comp) {
		if (val == null) {
			return null;
		}

		Annotation createAnnotation = ((ComponentCtrl) comp).getAnnotation("create");
		if (createAnnotation == null) {
			throw new WrongValueException(comp, "Missing create() annotation");
		}
		String className = createAnnotation.getAttribute("value");

		try {
			Class<?> clazz = Class.forName(className);
			Method method = clazz.getMethod("create", String.class);
			Object valueObject = method.invoke(null, val);
			return valueObject;
		} catch (ClassNotFoundException e) {
			throw new WrongValueException(comp, String.format(
				"Unable to find the '%s' class.",
				className));
		} catch (SecurityException e) {
			throw new WrongValueException(comp, String.format(
				"Security exception for 'create' method in '%s' class.",
				className));
		} catch (NoSuchMethodException e) {
			throw new WrongValueException(comp, String.format(
				"Unable to find 'create' method in '%s' class.",
				className));
		} catch (IllegalArgumentException e) {
			throw new WrongValueException(comp, String.format(
				"Illegal argument to execute 'create' method in '%s' class.",
				className));
		} catch (IllegalAccessException e) {
			throw new WrongValueException(comp, String.format(
				"Illegal access to execute 'create' method in '%s' class.",
				className));
		} catch (InvocationTargetException e) {
			if (e.getCause() instanceof IllegalArgumentException) {
				throw new WrongValueException(comp, Labels.getLabel(e.getCause().getMessage()));
			}
			throw new WrongValueException(comp, String.format(
				"Invocation exception while executing 'create' method in '%s' class: %s",
				className,
				e.getCause()));
		}
	}

	@Override
	public Object coerceToUi(Object val, Component comp) {
		if (val == null) {
			return null;
		}
		try {
			Method method = val.getClass().getMethod("asString");
			String valueString = (String) method.invoke(val);
			return valueString;
		} catch (SecurityException e) {
			throw new WrongValueException(comp, "Security exception for 'asString' method.");
		} catch (NoSuchMethodException e) {
			throw new WrongValueException(comp, "Unable to find 'asString' method.");
		} catch (IllegalArgumentException e) {
			throw new WrongValueException(comp, "Illegal argument for 'asString' method.");
		} catch (IllegalAccessException e) {
			throw new WrongValueException(comp, "Illegal access for 'asString' method.");
		} catch (InvocationTargetException e) {
			throw new WrongValueException(comp, "Invocation problem for 'asString' method.");
		}
	}

}
